#include "LevelGenerator.h"

#include <algorithm>
#include <chrono>
#include <random>

#include <main/MainConstants.h>
#include <math/MathConstants.h>
#include <math/Utils.h>
#include <graphic/Drawable.h>
#include <graphic/DrawManager.h>
#include <graphic\AnimatedDrawable.h>

#include <math/Point2.h>

#define SQUARE_DIM ( 618 )
#define HALF_SQUARE_DIM ( SQUARE_DIM / 2 )
#define LATERAL_SQUARE_VISIBLE_DIM_HEIGHT ( 70 )
#define LATERAL_SQUARE_VISIBLE_DIM_WIDTH ( 200 )

#define BEGIN_ROT_FRAMES ( MAX_FPS * 4 )

#define SEC_TO_ROTATE ( 1.0 )
#define TOTAL_ROTATION ( PI / 2.0 )
#define FRAMES_TO_ROTATE ( SEC_TO_ROTATE * MAX_FPS )
#define ROT_PER_FRAME (TOTAL_ROTATION / FRAMES_TO_ROTATE)

#define PAN_SPEED 1

LevelGenerator::LevelGenerator(ILevelGeneratorEvents* events) 
    : mFramesSinceRot(0)
    , mFramesWhileRot(0)
    , mCurRotation(0.0f)
    , mRotate(false)
	, events(events)
	, panLeft(false)
	, panFrameCount(0)
{
    std::vector<std::string> backgrounds;
    backgrounds.push_back("background0");
    backgrounds.push_back("background1");
    backgrounds.push_back("background2");
    backgrounds.push_back("background3");
    backgrounds.push_back("background4");
    backgrounds.push_back("background5");
    backgrounds.push_back("background6");
    backgrounds.push_back("background7");



    //
    // Create drawables
    //
	for(int i = 0; i < 9; ++i) {
        const std::string& filename = backgrounds[randomInt(0, 7)];
		mDrawables[i] = new Drawable(filename.c_str());
	}

	for(int i = 11; i < 14; ++i) {
        const std::string& filename = backgrounds[randomInt(0, 7)];
		mDrawables[i] = new Drawable(filename.c_str());
	}
   
	Entity* entity = new Entity;
    entity->type = DOOR;
	entity->pisycs = false;
	entity->staticObject = true;
    mDrawables[9] = new AnimatedDrawable("doorOpening", entity, 81, 37);

    mDrawables[10] = new AnimatedDrawable("doorOpening", entity, 81, 37);

    // Register drawables
    for (size_t i = 0; i < 11; ++i) {
        Globals::gDrawManager->addDrawable(mDrawables[i]);
    }
    
    // Shuffle drawables to generate a random level
    unsigned int seed = std::chrono::system_clock::now().time_since_epoch().count();
    std::shuffle (mDrawables, mDrawables + 9, std::default_random_engine(seed));

    // Reposition squares
    // 0: Center
    // 1: Left
    // 2: Right
    // 3: Top
    // 4: Bottom
	// 5: Top left
	// 6: Top right
	// 7: Bottom left
	// 8: Bottom right
    mDrawables[0]->updatePosition(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2);
    mDrawables[1]->updatePosition(- HALF_SQUARE_DIM + LATERAL_SQUARE_VISIBLE_DIM_WIDTH, 
                                  WINDOW_HEIGHT / 2);
    mDrawables[2]->updatePosition(WINDOW_WIDTH + HALF_SQUARE_DIM - LATERAL_SQUARE_VISIBLE_DIM_WIDTH, 
                                  WINDOW_HEIGHT / 2);

    mDrawables[3]->updatePosition(WINDOW_WIDTH / 2, 
                                  - HALF_SQUARE_DIM + LATERAL_SQUARE_VISIBLE_DIM_HEIGHT);
    mDrawables[4]->updatePosition(WINDOW_WIDTH / 2, 
                                  WINDOW_HEIGHT + HALF_SQUARE_DIM - LATERAL_SQUARE_VISIBLE_DIM_HEIGHT);


	mDrawables[5]->updatePosition(- HALF_SQUARE_DIM + LATERAL_SQUARE_VISIBLE_DIM_WIDTH + 3, 
								  WINDOW_HEIGHT / 2 + SQUARE_DIM);
    mDrawables[6]->updatePosition(WINDOW_WIDTH + HALF_SQUARE_DIM - LATERAL_SQUARE_VISIBLE_DIM_WIDTH, 
                                  WINDOW_HEIGHT / 2 + SQUARE_DIM);

    mDrawables[7]->updatePosition(WINDOW_WIDTH / 2 - SQUARE_DIM, 
                                  - HALF_SQUARE_DIM + LATERAL_SQUARE_VISIBLE_DIM_HEIGHT);
	mDrawables[8]->updatePosition(WINDOW_WIDTH + HALF_SQUARE_DIM - LATERAL_SQUARE_VISIBLE_DIM_WIDTH, 
                                  WINDOW_HEIGHT / 2 - SQUARE_DIM);


	// Add the door
	mDrawables[9]->updatePosition(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2 - HALF_SQUARE_DIM + 40);
	mDrawables[10]->updatePosition(WINDOW_WIDTH / 2 - HALF_SQUARE_DIM -40,  WINDOW_HEIGHT / 2, PI / 2);

	doorStartPos = mDrawables[9]->getPosition(); 

}

Point2 rotatePoint(float cx, float cy, float angle, Point2 p)
{
  float s = sin(angle);
  float c = cos(angle);

  // translate point back to origin:
  p.x -= cx;
  p.y -= cy;

  // rotate point
  float xnew = p.x * c + p.y * s;
  float ynew = -p.x * s + p.y * c;

  // translate point back:
  p.x = xnew + cx;
  p.y = ynew + cy;

  return p;
}


void LevelGenerator::update() {
	if(panLeft == false) {
		if (!mRotate) {
			// Wait for a new rotation
			if (mFramesSinceRot == BEGIN_ROT_FRAMES) {
				mFramesSinceRot = 0;
				mRotate = true;
				events->rotationStart();
			} else {
				++mFramesSinceRot;
			}
		} else {
			// Rotate
			if (mFramesWhileRot == FRAMES_TO_ROTATE) {
				mFramesWhileRot = 0;
				mRotate = false;
				events->rotationEnd();
			} else {
				++mFramesWhileRot;
				mCurRotation += ROT_PER_FRAME;			
				Point2 doorPoint(doorStartPos.mCenterPosX, doorStartPos.mCenterPosY);
				Point2 target = rotatePoint(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2, mCurRotation, doorPoint);
				mDrawables[9]->updatePosition(target.x, target.y, mCurRotation);
			}

			if (mCurRotation >= PI * 2 - .01) {
				mCurRotation = 0.0f;
				mRotate = false;
				mFramesWhileRot = 0;
				events->rotationEnd();
			}

			assert(mDrawables[0]);
			mDrawables[0]->updateOrientation(mCurRotation);

		}
	}

	// Check keys
	if(Globals::gKeyboardState.keyPressedEvent(SDL_SCANCODE_LEFT)) {
		// Init the left drawables
		
		mDrawables[11] = new Drawable("background0");
		const StaticData& topLeftPos = mDrawables[5]->getPosition();
		mDrawables[11]->updatePosition(topLeftPos.mCenterPosX - SQUARE_DIM, topLeftPos.mCenterPosY);

		mDrawables[12] = new Drawable("background0");
		const StaticData& leftPos = mDrawables[1]->getPosition();
		mDrawables[12]->updatePosition(leftPos.mCenterPosX - SQUARE_DIM, leftPos.mCenterPosY);

		mDrawables[13] = new Drawable("background0");
		const StaticData& botomLeft = mDrawables[7]->getPosition();
		mDrawables[13]->updatePosition(botomLeft.mCenterPosX - SQUARE_DIM, botomLeft.mCenterPosY);
		
		 for (size_t i = 11; i < 14; ++i) {
			 Globals::gDrawManager->addDrawable(mDrawables[i]);
		 }

		 // Start the "Pan" 
		 panLeft = true;	
	}


	if (panLeft) {
		events->cleanEnemies();
		const float step = SQUARE_DIM / MAX_FPS * PAN_SPEED;
		 for (size_t i = 0; i < 14; ++i) {
			 if(Globals::gDrawManager->isRegistered(mDrawables[i])) {
				 const StaticData& currentPos = mDrawables[i]->getPosition();
				 mDrawables[i]->updatePosition(currentPos.mCenterPosX + step, currentPos.mCenterPosY);
			 }
		 }

		if(panFrameCount >= SQUARE_DIM / step) {
			for (size_t i = 0; i < 14; ++i) {
				Globals::gDrawManager->removeDrawable(mDrawables[i]);
			}
			panLeft = false;
			events->finished();
		} else {
			panFrameCount++;
		}
	}

}